{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import zipfile\n",
    "import random\n",
    "import shutil\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras.optimizers import RMSprop\n",
    "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
    "from shutil import copyfile\n",
    "from os import getcwd\n",
    "from os import listdir\n",
    "import cv2\n",
    "from tensorflow.keras.layers import Conv2D, Input, ZeroPadding2D, BatchNormalization, Activation, MaxPooling2D, Flatten, Dense\n",
    "from tensorflow.keras.models import Model, load_model\n",
    "from tensorflow.keras.callbacks import TensorBoard, ModelCheckpoint\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import f1_score\n",
    "from sklearn.utils import shuffle\n",
    "import imutils\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.image  as mpimg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The number of images with facemask labelled 'yes': 690\n",
      "The number of images with facemask labelled 'no': 686\n"
     ]
    }
   ],
   "source": [
    "print(\"The number of images with facemask labelled 'yes':\",len(os.listdir('facemask-dataset/yes')))\n",
    "print(\"The number of images with facemask labelled 'no':\",len(os.listdir('facemask-dataset/no')))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of examples: 2751\n",
      "Percentage of positive examples: 50.163576881134134%, number of pos examples: 1380\n",
      "Percentage of negative examples: 49.836423118865866%, number of neg examples: 1371\n"
     ]
    }
   ],
   "source": [
    "def data_summary(main_path):\n",
    "    \n",
    "    yes_path = main_path+'yesreal'\n",
    "    no_path = main_path+'noreal'\n",
    "        \n",
    "    # number of files (images) that are in the the folder named 'yes' that represent tumorous (positive) examples\n",
    "    m_pos = len(listdir(yes_path))\n",
    "    # number of files (images) that are in the the folder named 'no' that represent non-tumorous (negative) examples\n",
    "    m_neg = len(listdir(no_path))\n",
    "    # number of all examples\n",
    "    m = (m_pos+m_neg)\n",
    "    \n",
    "    pos_prec = (m_pos* 100.0)/ m\n",
    "    neg_prec = (m_neg* 100.0)/ m\n",
    "    \n",
    "    print(f\"Number of examples: {m}\")\n",
    "    print(f\"Percentage of positive examples: {pos_prec}%, number of pos examples: {m_pos}\") \n",
    "    print(f\"Percentage of negative examples: {neg_prec}%, number of neg examples: {m_neg}\") \n",
    "    \n",
    "augmented_data_path = 'facemask-dataset/trial1/augmented data1/'    \n",
    "data_summary(augmented_data_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def split_data(SOURCE, TRAINING, TESTING, SPLIT_SIZE):\n",
    "    dataset = []\n",
    "    \n",
    "    for unitData in os.listdir(SOURCE):\n",
    "        data = SOURCE + unitData\n",
    "        if(os.path.getsize(data) > 0):\n",
    "            dataset.append(unitData)\n",
    "        else:\n",
    "            print('Skipped ' + unitData)\n",
    "            print('Invalid file i.e zero size')\n",
    "    \n",
    "    train_set_length = int(len(dataset) * SPLIT_SIZE)\n",
    "    test_set_length = int(len(dataset) - train_set_length)\n",
    "    shuffled_set = random.sample(dataset, len(dataset))\n",
    "    train_set = dataset[0:train_set_length]\n",
    "    test_set = dataset[-test_set_length:]\n",
    "       \n",
    "    for unitData in train_set:\n",
    "        temp_train_set = SOURCE + unitData\n",
    "        final_train_set = TRAINING + unitData\n",
    "        copyfile(temp_train_set, final_train_set)\n",
    "    \n",
    "    for unitData in test_set:\n",
    "        temp_test_set = SOURCE + unitData\n",
    "        final_test_set = TESTING + unitData\n",
    "        copyfile(temp_test_set, final_test_set)\n",
    "        \n",
    "        \n",
    "YES_SOURCE_DIR = \"facemask-dataset/trial1/augmented data1/yesreal/\"\n",
    "TRAINING_YES_DIR = \"facemask-dataset/trial1/augmented data1/training/yes1/\"\n",
    "TESTING_YES_DIR = \"facemask-dataset/trial1/augmented data1/testing/yes1/\"\n",
    "NO_SOURCE_DIR = \"facemask-dataset/trial1/augmented data1/noreal/\"\n",
    "TRAINING_NO_DIR = \"facemask-dataset/trial1/augmented data1/training/no1/\"\n",
    "TESTING_NO_DIR = \"facemask-dataset/trial1/augmented data1/testing/no1/\"\n",
    "split_size = .8\n",
    "split_data(YES_SOURCE_DIR, TRAINING_YES_DIR, TESTING_YES_DIR, split_size)\n",
    "split_data(NO_SOURCE_DIR, TRAINING_NO_DIR, TESTING_NO_DIR, split_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The number of images with facemask in the training set labelled 'yes': 1104\n",
      "The number of images with facemask in the test set labelled 'yes': 276\n",
      "The number of images without facemask in the training set labelled 'no': 1096\n",
      "The number of images without facemask in the test set labelled 'no': 275\n"
     ]
    }
   ],
   "source": [
    "print(\"The number of images with facemask in the training set labelled 'yes':\", len(os.listdir('facemask-dataset/trial1/augmented data1/training/yes1')))\n",
    "print(\"The number of images with facemask in the test set labelled 'yes':\", len(os.listdir('facemask-dataset/trial1/augmented data1/testing/yes1')))\n",
    "print(\"The number of images without facemask in the training set labelled 'no':\", len(os.listdir('facemask-dataset/trial1/augmented data1/training/no1')))\n",
    "print(\"The number of images without facemask in the test set labelled 'no':\", len(os.listdir('facemask-dataset/trial1/augmented data1/testing/no1')))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = tf.keras.models.Sequential([\n",
    "    tf.keras.layers.Conv2D(100, (3,3), activation='relu', input_shape=(150, 150, 3)),\n",
    "    tf.keras.layers.MaxPooling2D(2,2),\n",
    "    \n",
    "    tf.keras.layers.Conv2D(100, (3,3), activation='relu'),\n",
    "    tf.keras.layers.MaxPooling2D(2,2),\n",
    "    \n",
    "    tf.keras.layers.Flatten(),\n",
    "    tf.keras.layers.Dropout(0.5),\n",
    "    tf.keras.layers.Dense(50, activation='relu'),\n",
    "    tf.keras.layers.Dense(2, activation='softmax')\n",
    "])\n",
    "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 2200 images belonging to 2 classes.\n",
      "Found 551 images belonging to 2 classes.\n"
     ]
    }
   ],
   "source": [
    "TRAINING_DIR = \"facemask-dataset/trial1/augmented data1/training\"\n",
    "train_datagen = ImageDataGenerator(rescale=1.0/255,\n",
    "                                   rotation_range=40,\n",
    "                                   width_shift_range=0.2,\n",
    "                                   height_shift_range=0.2,\n",
    "                                   shear_range=0.2,\n",
    "                                   zoom_range=0.2,\n",
    "                                   horizontal_flip=True,\n",
    "                                   fill_mode='nearest')\n",
    "\n",
    "train_generator = train_datagen.flow_from_directory(TRAINING_DIR, \n",
    "                                                    batch_size=10, \n",
    "                                                    target_size=(150, 150))\n",
    "VALIDATION_DIR = \"facemask-dataset/trial1/augmented data1/testing\"\n",
    "validation_datagen = ImageDataGenerator(rescale=1.0/255)\n",
    "\n",
    "validation_generator = validation_datagen.flow_from_directory(VALIDATION_DIR, \n",
    "                                                         batch_size=10, \n",
    "                                                         target_size=(150, 150))\n",
    "checkpoint = ModelCheckpoint('model-{epoch:03d}.model',monitor='val_loss',verbose=0,save_best_only=True,mode='auto')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:sample_weight modes were coerced from\n",
      "  ...\n",
      "    to  \n",
      "  ['...']\n",
      "WARNING:tensorflow:sample_weight modes were coerced from\n",
      "  ...\n",
      "    to  \n",
      "  ['...']\n",
      "Train for 220 steps, validate for 56 steps\n",
      "Epoch 1/30\n",
      "220/220 [==============================] - 237s 1s/step - loss: 0.0922 - acc: 0.9682 - val_loss: 0.1251 - val_acc: 0.9546\n",
      "Epoch 2/30\n",
      "219/220 [============================>.] - ETA: 1s - loss: 0.1030 - acc: 0.9685INFO:tensorflow:Assets written to: model-002.model\\assets\n",
      "220/220 [==============================] - 247s 1s/step - loss: 0.1026 - acc: 0.9686 - val_loss: 0.1050 - val_acc: 0.9619\n",
      "Epoch 3/30\n",
      "220/220 [==============================] - 263s 1s/step - loss: 0.0731 - acc: 0.9786 - val_loss: 0.5988 - val_acc: 0.7550\n",
      "Epoch 4/30\n",
      "220/220 [==============================] - 231s 1s/step - loss: 0.0733 - acc: 0.9741 - val_loss: 0.1133 - val_acc: 0.9528\n",
      "Epoch 5/30\n",
      "220/220 [==============================] - 231s 1s/step - loss: 0.1414 - acc: 0.9541 - val_loss: 0.1225 - val_acc: 0.9564\n",
      "Epoch 6/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.1031 - acc: 0.9645 - val_loss: 0.1323 - val_acc: 0.9583\n",
      "Epoch 7/30\n",
      "219/220 [============================>.] - ETA: 0s - loss: 0.0958 - acc: 0.9699INFO:tensorflow:Assets written to: model-007.model\\assets\n",
      "220/220 [==============================] - 238s 1s/step - loss: 0.0967 - acc: 0.9695 - val_loss: 0.0932 - val_acc: 0.9655\n",
      "Epoch 8/30\n",
      "220/220 [==============================] - 232s 1s/step - loss: 0.1023 - acc: 0.9655 - val_loss: 0.1128 - val_acc: 0.9564\n",
      "Epoch 9/30\n",
      "220/220 [==============================] - 234s 1s/step - loss: 0.0886 - acc: 0.9691 - val_loss: 0.0934 - val_acc: 0.9601\n",
      "Epoch 10/30\n",
      "220/220 [==============================] - 234s 1s/step - loss: 0.0940 - acc: 0.9686 - val_loss: 0.1654 - val_acc: 0.9401\n",
      "Epoch 11/30\n",
      "219/220 [============================>.] - ETA: 1s - loss: 0.1115 - acc: 0.9680INFO:tensorflow:Assets written to: model-011.model\\assets\n",
      "220/220 [==============================] - 238s 1s/step - loss: 0.1111 - acc: 0.9682 - val_loss: 0.0861 - val_acc: 0.9637\n",
      "Epoch 12/30\n",
      "220/220 [==============================] - 234s 1s/step - loss: 0.0612 - acc: 0.9809 - val_loss: 0.1659 - val_acc: 0.9365\n",
      "Epoch 13/30\n",
      "220/220 [==============================] - 234s 1s/step - loss: 0.0995 - acc: 0.9709 - val_loss: 0.1097 - val_acc: 0.9583\n",
      "Epoch 14/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.0572 - acc: 0.9782 - val_loss: 0.1252 - val_acc: 0.9510\n",
      "Epoch 15/30\n",
      "220/220 [==============================] - 234s 1s/step - loss: 0.1090 - acc: 0.9650 - val_loss: 0.1397 - val_acc: 0.9583\n",
      "Epoch 16/30\n",
      "220/220 [==============================] - 235s 1s/step - loss: 0.0510 - acc: 0.9809 - val_loss: 0.1068 - val_acc: 0.9528\n",
      "Epoch 17/30\n",
      "220/220 [==============================] - 232s 1s/step - loss: 0.0508 - acc: 0.9805 - val_loss: 0.1558 - val_acc: 0.9419\n",
      "Epoch 18/30\n",
      "220/220 [==============================] - 232s 1s/step - loss: 0.0497 - acc: 0.9850 - val_loss: 0.1015 - val_acc: 0.9619\n",
      "Epoch 19/30\n",
      "220/220 [==============================] - 232s 1s/step - loss: 0.1081 - acc: 0.9677 - val_loss: 0.1414 - val_acc: 0.9365\n",
      "Epoch 20/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.0913 - acc: 0.9686 - val_loss: 0.1166 - val_acc: 0.9528\n",
      "Epoch 21/30\n",
      "220/220 [==============================] - 236s 1s/step - loss: 0.0708 - acc: 0.9755 - val_loss: 0.1102 - val_acc: 0.9601\n",
      "Epoch 22/30\n",
      "220/220 [==============================] - 232s 1s/step - loss: 0.0564 - acc: 0.9782 - val_loss: 0.1580 - val_acc: 0.9419\n",
      "Epoch 23/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.0493 - acc: 0.9823 - val_loss: 0.1048 - val_acc: 0.9637\n",
      "Epoch 24/30\n",
      "220/220 [==============================] - 231s 1s/step - loss: 0.0620 - acc: 0.9809 - val_loss: 0.1151 - val_acc: 0.9528\n",
      "Epoch 25/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.0828 - acc: 0.9768 - val_loss: 0.1617 - val_acc: 0.9437\n",
      "Epoch 26/30\n",
      "220/220 [==============================] - 233s 1s/step - loss: 0.0438 - acc: 0.9886 - val_loss: 0.2205 - val_acc: 0.9292\n",
      "Epoch 27/30\n",
      "220/220 [==============================] - 287s 1s/step - loss: 0.0608 - acc: 0.9805 - val_loss: 0.1159 - val_acc: 0.9528\n",
      "Epoch 28/30\n",
      "220/220 [==============================] - 276s 1s/step - loss: 0.0536 - acc: 0.9850 - val_loss: 0.1131 - val_acc: 0.9601\n",
      "Epoch 29/30\n",
      "220/220 [==============================] - 257s 1s/step - loss: 0.0495 - acc: 0.9882 - val_loss: 0.0949 - val_acc: 0.9655\n",
      "Epoch 30/30\n",
      "220/220 [==============================] - 231s 1s/step - loss: 0.0368 - acc: 0.9886 - val_loss: 0.1072 - val_acc: 0.9619\n"
     ]
    }
   ],
   "source": [
    "history = model.fit_generator(train_generator,\n",
    "                              epochs=30,\n",
    "                              validation_data=validation_generator,\n",
    "                              callbacks=[checkpoint])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "face_clsfr=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "labels_dict={0:'without_mask',1:'with_mask'}\n",
    "color_dict={0:(0,0,255),1:(0,255,0)}\n",
    "\n",
    "size = 4\n",
    "webcam = cv2.VideoCapture(0) #Use camera 0\n",
    "\n",
    "# We load the xml file\n",
    "classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')\n",
    "\n",
    "while True:\n",
    "    (rval, im) = webcam.read()\n",
    "    im=cv2.flip(im,1,1) #Flip to act as a mirror\n",
    "\n",
    "    # Resize the image to speed up detection\n",
    "    mini = cv2.resize(im, (im.shape[1] // size, im.shape[0] // size))\n",
    "\n",
    "    # detect MultiScale / faces \n",
    "    faces = classifier.detectMultiScale(mini)\n",
    "\n",
    "    # Draw rectangles around each face\n",
    "    for f in faces:\n",
    "        (x, y, w, h) = [v * size for v in f] #Scale the shapesize backup\n",
    "        #Save just the rectangle faces in SubRecFaces\n",
    "        face_img = im[y:y+h, x:x+w]\n",
    "        resized=cv2.resize(face_img,(150,150))\n",
    "        normalized=resized/255.0\n",
    "        reshaped=np.reshape(normalized,(1,150,150,3))\n",
    "        reshaped = np.vstack([reshaped])\n",
    "        result=model.predict(reshaped)\n",
    "        #print(result)\n",
    "        \n",
    "        label=np.argmax(result,axis=1)[0]\n",
    "      \n",
    "        cv2.rectangle(im,(x,y),(x+w,y+h),color_dict[label],2)\n",
    "        cv2.rectangle(im,(x,y-40),(x+w,y),color_dict[label],-1)\n",
    "        cv2.putText(im, labels_dict[label], (x, y-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(255,255,255),2)\n",
    "        \n",
    "    # Show the image\n",
    "    cv2.imshow('LIVE',   im)\n",
    "    key = cv2.waitKey(10)\n",
    "    # if Esc key is press then break out of the loop \n",
    "    if key == 27: #The Esc key\n",
    "        break\n",
    "# Stop video\n",
    "webcam.release()\n",
    "\n",
    "# Close all started windows\n",
    "cv2.destroyAllWindows()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
